import { OTWSceneManager } from "./OTWSceneManager";
import { FrameSyncExecutor } from "../common/FrameSyncExecutor";
import { ConnectionInputOperate } from "../../shared/gameClient/GameSyncFrame";

import { IArea, ICountry, IMap, InputType, IPlayer, ITroop } from "../../shared/gameClient/games/OccupationTheWarModels";
import { Component, _decorator, Node, Prefab, instantiate, PhysicsSystem, Vec3, UICoordinateTrackerComponent, EventHandler, EventTouch, SpriteComponent, Sprite, SpriteFrame, Collider, ICollisionEvent, ITriggerEvent } from "cc";
import { OTWAreaData } from "./Data/OTWAreaData";
import { OTWAreaComponent } from "./Components/OTWAreaComponent";
import { OTWTroopData } from "./Data/OTWTroopData";
import { PHY_GROUP } from "./Const";
import { LinkMainNodeComponent } from "../common/LinkMainNodeComponent";
import { OTWGameData } from "./Data/OTWGameData";
import { OTWObjectComponent } from "./Components/OTWObjectComponent";
import { OTWGameResource } from "./OTWGameResource";
import { OTWTroopManager } from "./OTWTroopManager";
import { GameClientFrameSyncConnectAdp } from "../common/GameClientFrameSyncConnectAdp";
const { ccclass, property } = _decorator;

@ccclass('OTWGameManager')
export class OTWGameManager extends Component {

    @property(OTWSceneManager)
    public SceneMgr!: OTWSceneManager;
    @property(OTWGameResource)
    public GameRes!: OTWGameResource;
    @property(OTWTroopManager)
    public TroopMgr!: OTWTroopManager;


    private frameSyncExecutor: FrameSyncExecutor | null = null;

    private gameData: OTWGameData = {
        map: null,
        allAreaData: [],
        allAttackingTroopData: {},
        myPlayer: null,
        myCountryId: null,
    };
    private getGameData = () => this.gameData;

    onLoad() {
        this.SceneMgr.startGameEventHandlers.addHandler(() => this.enterGame());
        this.SceneMgr.stopGameEventHandlers.addHandler(() => this.exitGame());
        this.TroopMgr.getGameData = this.getGameData;
        this.TroopMgr.onCountryLose = async (c) => {
            if (this.gameData.myCountryId && this.gameData.myCountryId === c.countryId) {
                //如果是自己的国家输了,则游戏结束
                await this.exitGame();
                this.SceneMgr.showLoseGame();
                return;
            }

            var firstCountryId = this.gameData.map?.allArea[0].countryId;
            if (firstCountryId && !this.gameData.map?.allArea.find(a => a.countryId !== firstCountryId)) {
                //如果只剩下一个国家，则有玩家胜利了
                if (this.gameData.myCountryId && this.gameData.myCountryId === firstCountryId) {
                    //是自己!胜利!
                    await this.exitGame();
                    this.SceneMgr.showWinGame();
                    return;
                } else {
                    //否则,一般是观众模式,直接退出了
                    await this.exitGame();
                }
            }
        };
    }

    start() {
        this.SceneMgr.TouchCtrl.touchRayGetObjHandler = (isFrom, ray) => {
            if (!this.gameData.myPlayer) return null;
            if (!PhysicsSystem.instance.raycastClosest(ray, PHY_GROUP.Select)) {
                //没点到area节点,忽略
                return null;
            }
            var collider = PhysicsSystem.instance.raycastClosestResult.collider;
            var node = collider.node;
            var mainNode = node?.getComponent(LinkMainNodeComponent)?.MainNode;
            var areaInfoComp = mainNode?.getComponent(OTWAreaComponent);
            if (!areaInfoComp) return null;
            if (isFrom) {
                if (this.gameData.myPlayer.countryId != areaInfoComp.areaData?.area.countryId) {
                    //不是自己国家的地域,不能操作
                    return null;
                }
            }
            return areaInfoComp.areaData?.area;
        };
        this.SceneMgr.TouchCtrl.touchFromToHandler = (fromArea: IArea, toArea: IArea) => {
            if (!fromArea || !toArea) {
                return;
            }

            this.frameSyncExecutor?.sendInputFrame(
                {
                    inputType: InputType.Attack,
                    countryId: fromArea.countryId,
                    fromAreaIndex: fromArea.areaIndex,
                    toAreaIndex: toArea.areaIndex,
                }
            );
        };

        //关闭物理的自动模拟, 因为帧同步的数据需要用到物理计算, 所以让物理模拟放在逻辑帧里模拟实现碰撞检测, 渲染帧里只做显示(也方便实现平滑/慢动作等)
        PhysicsSystem.instance.autoSimulation = false;
    }

    public enterGame() {
        this.frameSyncExecutor = new FrameSyncExecutor(
            new GameClientFrameSyncConnectAdp(this.SceneMgr.gameClient!), this,
            (stateData, stateFrameIndex) => this.onSyncStateData(stateData, stateFrameIndex),
            (dt) => this.executeOneFrame(dt),
            () => this.gameData.map);
    }
    public async exitGame() {
        await this.frameSyncExecutor?.dispose();
        this.frameSyncExecutor = null;
        this.clearMap();
    }

    clearMap() {
        this.gameData.myPlayer = null;
        this.gameData.myCountryId = null;
        this.gameData.map = null;

        this.gameData.allAreaData.forEach(n => {
            n.areaNode.parent?.removeChild(n.areaNode);
            n.flagNode.parent?.removeChild(n.flagNode);
        });
        this.gameData.allAreaData.length = 0;

        for (const troopId in this.gameData.allAttackingTroopData) {
            const n = this.gameData.allAttackingTroopData[troopId];
            n.renderNode.parent?.removeChild(n.renderNode);
            n.troopNode.parent?.removeChild(n.troopNode);
            n.flagNode.parent?.removeChild(n.flagNode);
            delete this.gameData.allAttackingTroopData[troopId];
        }
    }
    createArea(area: IArea, country: ICountry | null): OTWAreaData {
        var areaInfo = OTWAreaComponent.createNode(
            this.GameRes.AreaPrefab!, this.GameRes.AreaFlagPrefab!, this.getGameData,
            area, country, this.SceneMgr.MainCamera!, this.node,
            this.SceneMgr.GameObjFlagUI!, this.SceneMgr.GameObjContainer!);
        this.gameData.allAreaData[areaInfo.area.areaIndex] = areaInfo;

        //地域碰撞事件不需要处理,先忽略
        //var collider = areaInfo.areaNode.getComponentInChildren(Collider);
        //collider?.on('onTriggerEnter', (event: ITriggerEvent) => { this.onTriggerEnter(event) });

        return areaInfo;
    }


    onSyncStateData(stateData: any, stateFrameIndex: number) {
        //需要同步状态数据,当作重新初始化游戏数据
        this.clearMap();
        this.gameData.map = stateData;
        if (!this.gameData.map || !this.gameData.map.allPlayer) return;

        this.gameData.myPlayer = this.gameData.map.allPlayer[this.SceneMgr.gameClient!.connectionId];
        this.gameData.myCountryId = this.gameData.myPlayer?.countryId;

        for (let i = 0; i < this.gameData.map.allArea.length; i++) {
            const a = this.gameData.map.allArea[i];
            var country = a.countryId ? this.gameData.map.allCountry[a.countryId] : null;
            this.createArea(a, country);
        }
        for (var troopId in this.gameData.map.allAttackingTroop) {
            var t = this.gameData.map.allAttackingTroop[troopId];
            this.TroopMgr.restoreTroop(t);
        }
    }


    execInput_NewPlayer(connId: string, inputFrame: ConnectionInputOperate, dt: number, frameIndex: number): void {
        console.log("execInput_NewPlayer", connId, inputFrame, frameIndex);
        if (!this.gameData.map) return;
        let player: IPlayer | undefined;
        let country: ICountry;

        let reconnectOldConnId = inputFrame.reconnectOldConnId;
        if (reconnectOldConnId) {
            //如果是断线重连的,则找到之前的新分配
            player = this.gameData.map.allPlayer[reconnectOldConnId];
            delete this.gameData.map.allPlayer[reconnectOldConnId];
            if (player) {
                //确实找到之前的玩家了, 则更新一下新的连接id
                player.connId = connId;
                this.gameData.map.allPlayer[player.connId] = player;
                country = this.gameData.map.allCountry[player.countryId];
                if (country) {
                    //有国家,则更新一下国家关联的玩家连接ID
                    country.playerConnId = connId;
                }
            }
        } else {

            //分配国家和观战的逻辑
            var emptyCountry: ICountry | null = null;
            for (let i = 0; i < this.gameData.map.allArea.length; i++) {
                //要循环所有地域, 关联的国家没所属玩家的, 才认为是可分配国家
                var a = this.gameData.map.allArea[i];
                if (!a.countryId) continue;
                var c = this.gameData.map.allCountry[a.countryId];
                if (c && !c.playerConnId) {
                    emptyCountry = c;
                    break;
                }
            }
            if (emptyCountry) {
                //有分配到国家,则新增玩家对象,并关联国家
                player = {
                    connId: connId,
                    userName: inputFrame.userName,
                    countryId: emptyCountry.countryId,
                };
                this.gameData.map.allPlayer[player.connId] = player;
                emptyCountry.playerConnId = player.connId;
            }
        }

        if (player) {
            //有产生玩家
            if (connId == this.SceneMgr.gameClient?.connectionId) {
                //当前连接是自己,更新我的玩家信息
                this.gameData.myPlayer = player;
                this.gameData.myCountryId = player.countryId;
            }
            for (let i = 0; i < this.gameData.map.allArea.length; i++) {
                var a = this.gameData.map.allArea[i];
                if (a.countryId != player.countryId) continue;
                this.gameData.allAreaData[a.areaIndex]?.flagComp?.updateShow();
            }
        } else {
            if (connId == this.SceneMgr.gameClient?.connectionId) {
                //新连接没关联玩家,如果连接是自己,则进入观众模式
                this.gameData.myPlayer = null;
                this.gameData.myCountryId = null;
                this.SceneMgr.setStep(6);
            }
        }

    }
    execInput_RemovePlayer(connId: string, inputFrame: ConnectionInputOperate, dt: number, frameIndex: number): void {
        console.log("execInput_RemovePlayer", connId, inputFrame, frameIndex);
        if (connId == this.SceneMgr.gameClient?.connectionId) {
            //如果当前连接是自己,则清理我的玩家数据
            this.gameData.myPlayer = null;
            this.gameData.myCountryId = null;
        }
        if (!this.gameData.map) return;
        //玩家离开,将该玩家改成电脑玩家
        var player = this.gameData.map.allPlayer[connId];
        if (!player) {
            //观众离开,不会有玩家
            return;
        }
        delete this.gameData.map.allPlayer[connId];
        var country = this.gameData.map.allCountry[player.countryId];
        if (!country) {
            console.warn(`countryId=${player.countryId}不存在国家,这有问题!`);
            return;
        }
        country.playerConnId = null;
        //更新flag
        for (let i = 0; i < this.gameData.map.allArea.length; i++) {
            var a = this.gameData.map.allArea[i];
            if (a.countryId != player.countryId) continue;
            this.gameData.allAreaData[a.areaIndex]?.flagComp?.updateShow();
        }
    }

    execInput_Attack(connId: string, inputFrame: ConnectionInputOperate, dt: number, frameIndex: number): void {
        if (!this.gameData.map) return;
        var country = inputFrame.countryId ? this.gameData.map.allCountry[inputFrame.countryId] : null;
        if (!country) {
            //没有国家,则不能发动攻击
            return;
        }
        var fromAreaData = this.gameData.allAreaData[inputFrame.fromAreaIndex];
        var toAreaData = this.gameData.allAreaData[inputFrame.toAreaIndex];
        if (fromAreaData.area.troopsCurr <= 0) return;//兵力为0则不能发动攻击

        var troopData = this.TroopMgr.createTroop(fromAreaData.area, toAreaData.area, fromAreaData.area.troopsCurr);
        //兵力全部转出去,清空并刷新
        fromAreaData.area.troopsCurr = 0;
        fromAreaData.area.troopsCurrCalculate = 0;
        fromAreaData.flagComp.updateShow();
    }


    executeOneFrame(dt: number): void {
        //运算增兵
        for (let i = 0; i < this.gameData.allAreaData.length; i++) {
            this.gameData.allAreaData[i].areaComp.updateData(dt);
        }
        //运算移动以及产生的效果
        for (let troopId in this.gameData.allAttackingTroopData) {
            this.gameData.allAttackingTroopData[troopId].troopComp.updateData(dt);
        }
        //物理分离
        PhysicsSystem.instance.syncSceneToPhysics();
        PhysicsSystem.instance.step(dt, dt);
        PhysicsSystem.instance.emitEvents();
    }

}